home *** CD-ROM | disk | FTP | other *** search
-
- Lesson 8.
-
- This lesson and the following one will examine how to use the program
- structure - as opposed to data structure - reserved words.
-
- Lets start with the looping structures:
-
- do repeated_statement while ( logical_expression );
-
- repeated_statement, which may be a block of code, will be executed
- repetitively until the logical_expression, becomes false. If you have been
- exposed to ( corrupted by? ) another language remember that there is no
- `until' test at the end of a loop. Note that the repeated_statement is always
- executed once irrespective of the state of the logical_expression.
-
- while ( logical_expression ) repeated_statement;
-
- repeated_statement is executed repetitively while the logical_expression
- is true. Once again statement may be a block of code. Note that if the
- logical_expression evaluates to FALSE then the repeated_statement is NEVER
- executed.
-
- Associated with the looping structures are the control words:
-
- break;
- continue;
-
- break; allows you to leave a loop in the middle of a block, and
- continue; allows you to re-start it from the top.
-
- Finally we must not forget the most common and useful looping construct:
-
- for ( initialising statement; logical_expression; incremental_statement )
- repeated_statement;
-
- Some further explanation is needed. The initialising statement is
- executed once, but to allow for the need to initialise several separate
- variables the assignment statements may be separated by commas. The
- logical_expression must be true for the loop to run, and the
- incremental_statement is executed once each time the loop is run.
- The for statement is completely general and may, for example, be used to
- manipulate a set of pointers to operate on a linked list.
-
- Some examples.
-
- A do loop program.
-
- #ident "@(#) do_demo.c - An example of the do loop"
-
- #include <stdio.h>
-
- main()
- {
- char character;
-
- character = 'a';
-
- do printf ( "%c", character ); while ( character++ < 'z' );
- printf ( "\n" );
- }
-
- Fairly obviously it prints:
-
- abcdefghijklmnopqrstuvwxyz
-
- A while loop example.
-
- #ident "@(#) while_demo.c - An example of the while loop"
-
- #include <stdio.h>
-
- main()
- {
- char character;
-
- character = 'a';
-
- while ( character <= 'z' ) printf ( "%c", character++ );
- printf ( "\n" );
- }
-
- Its output is exactly the same as the previous example:
-
- abcdefghijklmnopqrstuvwxyz
-
- In this totally trivial case it is irrelevant which program structure
- you use, however you should note that in the `do' program structure the
- repeated statement is always executed at least once.
- A for loop example.
-
- The `for' looping structure.
-
- #ident "@(#) for_demo.c - An example of the for loop"
-
- #include <stdio.h>
-
- main()
- {
- char character;
-
- for ( character = 'a'; character <= 'z' ; character++ )
- {
- printf ( "%c", character );
- }
- printf ( "\n" );
- }
-
- Surprise, Surprise!
-
- abcdefghijklmnopqrstuvwxyz
-
- You should be aware that in all the looping program structures, the
- repeated statement can be a null statement ( either just a `;' or the
- reserved word `continue;' ). This means that it is possible to - for
- example - position a pointer, or count up some items of something or other.
- It isn't particularly easy to think up a trivial little program which
- demonstrates this concept, however the two `for' loops give some indication
- of the idea.
-
- #ident "@(#) pointer_demo.c - Pointer operations with the for loop"
-
- #include <stdio.h>
-
- main()
- {
- char character, *character_pointer, alphabets [ 53 ];
-
- for ( character = 'a', character_pointer = alphabets; /* Start conditions */
- character <= 'z'; /* Run while true */
- *character_pointer++ = character++ /* All the work */
- )TRUE continue;
-
- for ( character = 'A'; /* character_pointer is at the right place already */
- character <= 'Z';
- *character_pointer++ = character++
- ) continue;
-
- *character_pointer = (char) '\000'; /* NULL character to terminate string. */
-
- printf ( "%s\n\n", alphabets );
- }
-
- Another Surprise!
-
- abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
-
- So much for the looping structures provided by the `C' language. The
- other main structures required to program a computer are the ones which
- alter the program flow. These are the switch, and the if and its extension
- the if ... else combination. More demo programs are much the best way of
- getting the message across to you, so here they are, first the if construct.
-
- #ident "if_demo.c"
-
- #include <stdio.h>
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- if ( argc > 1 ) printf ( "You have initiated execution with arguments."};
- }
-
- And the if ... else demo.
-
- #ident "if_else_demo.c"
- /*
- ** The Language #define could go in the compiler invocation line if desired.
- */
-
- #define ENGLISH
-
- #include <stdio.h>
-
- /*
- ** The message and text fragments output by the program.
- */
-
- char *messages[] =
- {
- #if defined( ENGLISH )
- #ident "@(#)ENGLISH Version"
- "\nUsage: if_else_demo <numeric argument 1> <numeric argument 2>\n\n",
- "The first argument is ",
- "the second",
- "equal to ",
- "bigger than ",
- "smaller than "
- #endif
-
- #if defined( FRANCAIS )
- #ident "@(#)FRENCH Version"
-
- put the French translation in here so that we are ready to export to
- French speaking Countries. I'd be grateful if a French speaker could
- make the translation for me.
-
- #endif
- };
-
- /*
- ** Meaningful words defined to constants
- */
-
- #define USAGE 0
- #define FIRST 1
- #define SECOND 2
- #define EQUAL 3
- #define BIGGER 4
- #define SMALLER 5
-
- #define SUCCESS 0
- #define FAILURE 1
-
- /*
- ** We need this more than once so it can be put in a function.
- */
-
- void usage()
- {
- printf ( messages[USAGE]);
- exit ( FAILURE );
- }
-
- /*
- ** Main program function starts here. ( At the top of a page no less! )
- */
-
- int main ( argc, argv )
- int argc;
- char **argv;
- {
- int message_index;
- double i, j, strtod();
- char *ptr;
-
- if ( argc != 3 ) usage(); /* have we been given the right */
- /* number of arguments. */
- i = strtod ( argv[1], &ptr); /* Convert to a double float. */
- if ( ptr == argv[1] ) usage(); /* Successful conversion? */
- j = strtod ( argv[2], &ptr); /* Convert to a double float. */
- if ( ptr == argv[2] ) usage(); /* Successful conversion? */
-
- /*
- ** This statement uses the "ternary conditional assignment" language
- ** construction to assign the value required to the message indexing variable.
- ** Note that this concept is efficient in both the generation of machine code
- ** output ( compile the program with a -S switch and have a look ) and in the
- ** ease with which it can be understood. The assignment is obvious instead of
- ** being buried under a litter of `if' and `else' keywords.
- */
-
- message_index = ( i == j ) ? EQUAL : ( i > j ) ? BIGGER : SMALLER;
-
- /*
- ** Now print the message.
- */
-
- (void) printf ( "\n%s%s%s\n\n", /* Format string specifying 3 strings. */
- messages[ FIRST ], /* Address of string. */
- messages[ message_index ], /* ditto. */
- messages[ SECOND ] /* ditto. */
- );
- return ( SUCCESS );
- }
-
- Well as you can no doubt gather it simply compares two numbers on the
- command line and ejects a little message depending on the relative magnitude
- of the numbers. In the UNIX tradition the help message is perhaps somewhat
- terse, but it serves the purpose of getting you - the student - to think
- about the importance of creating programs which always cope with nonsensical
- input in a civilised way. Here are the lines of output.
-
- Usage: if_else_demo <numeric argument 1> <numeric argument 2>
-
- The first argument is equal to the second
-
- The first argument is smaller than the second
-
- The first argument is bigger than the second
-
- Now that the international community is shrinking with vastly improved
- telecommunications, it is perhaps a good idea to think carefully about
- creating programs which can talk in many languages to the users. The method
- of choice is - I believe - that presented above. The #if defined( LANGUAGE )
- gives us an easy method of changing the source code to suit the new sales
- area. Another possibility is to put all the text output needed from a program
- into a file. The file would have to have a defined layout and some consistent
- way of `getting at' the message strings.
-
- From a commercial point of view this may or may not be a good business plan.
- Quite definitely it is an absolute no no to scatter a mass of string literals
- containing the messages and message fragments all over your program script.
-
- There are two more methods of altering the program flow.
-
- 1 ) The goto a label.
- 2 ) The setjump / longjmp library routines.
-
- The concept of the go to a label construction has had reams of literary
- verbiage written about it and this author does not intend to add to the pile.
- Suffice it to say that a goto is a necessary language construct. There are a
- few situations which require the language to have ( in practice ) some form of
- unconditional jump. Treat this statement with great caution if you wish your
- code to be readable by others. An example of legitimate use.
-
- for ( a = 0; a < MATRIX_SIZE; a++ )
- {
- for ( b = 0; b < MATRIX_SIZE; b++ )
- {
- if ( process ( matrix, a, b )) goto bad_matrix;
- }
- }
- return ( OK );
-
- bad_matrix:
-
- perror ( progname, "The data in the matrix seems to have been corrupted" );
- return ( BAD );
-
- This is one of the very few "legitimate" uses of goto, as there is no
- "break_to_outer_loop" in `C'. Note that some compilers complain if the label
- is not immediately followed by a statement. If your compiler is one of these
- naughty ones, you can put either a `;' or a pair of braces `{}' after the
- `:' as a null statement.
-
- An example of a program package which makes extensive use of the goto is the
- rz and sz modem communications protocol implementation by Chuck Forsberg of
- Omen Technology. You should download it and study the code, but do remember
- that the proof of the pudding argument must apply as the rz & sz system has
- become extremely popular in its application because it works so well.
-
- The other method of changing program flow is the setjump and longjmp pair of
- library functions. The idea is to provide a method of recovery from errors
- which might be detected anywhere within a large program - perhaps a compiler,
- interpreter or large data acquisition system. Here is the trivial example:
-
- #ident "set_jmp_demo.c"
-
- #include <stdio.h>
- #include <setjmp.h>
-
- jmp_buf save;
-
- main()
- {
- char c;
-
- for ( ;; ) /* This is how you set up a continuous loop.
- */
- {
- switch ( setjmp( save ))
- {
- case 0:
- printf ( "We get a zero returned from setjmp on setup.\n\n");
- break; /* This is the result from setting up. */
-
- case 1:
- printf ( "NORMAL PROGRAM OPERATION\n\n" );
- break;
-
- case 2:
- printf ( "WARNING\n\n" );
- break;
-
- case 3:
- printf ( "FATAL ERROR PROGRAM TERMINATED\n\nReally Terminate? y/n: " );
- fflush ( stdout );
- scanf ( "%1s", &c );
- c = tolower ( c );
- if ( c == 'y' ) return ( 1 );
- printf ( "\n" );
- break;
-
- default:
- printf ( "Should never return here.\n" );
- break;
- }
- process ();
- }
- }
-
- process ()
- {
- int i;
-
- printf ( "Input a number to simulate an error condition: " );
- fflush ( stdout );
- scanf ( "%d", &i );
- i %= 3;
- i++; /* So that we call longjmp with 0 < i < 4 */
- longjmp ( save, i);
- }
-
- Although in this silly little demo the call to longjmp is in the same file
- as the call to setjmp, this does not have to be the case, and in the practical
- situation the call to longjmp will be a long way from the call to setjmp. The
- mechanism is that setjmp saves the entire state of the computer's CPU in a
- buffer declared in the jmp_buf save; statement and longjmp restores it exactly
- with the exception of the register which carries the return value from longjmp.
- This value is the same as the second argument in the longjmp call - i in our
- little demo. This means, of course, that the stack and frame pointer registers
- are reset to the old values and all the local variables being used at the time
- of the longjmp call are going to be lost forever. One consequence of this is
- that any pointer to memory allocated from the heap will also be lost, and
- you will be unable to access the data stored in the buffer. This is what the
- jargonauts call "memory leakage", and is really very difficult bug to find.
- Your program runs out of dynamic memory long before it should. Take care.
- So you have to keep a record of the buffers' addresses and free them
- before the call to longjmp.
-
- More details later on when we learn about the heap memory allocation routines.
-
- Copyright notice:-
-
- (c) 1993 Christopher Sawtell.
-
- I assert the right to be known as the author, and owner of the
- intellectual property rights of all the files in this material,
- except for the quoted examples which have their individual
- copyright notices. Permission is granted for onward copying,
- but not modification, of this course and its use for personal
- study only, provided all the copyright notices are left in the
- text and are printed in full on any subsequent paper reproduction.
-
- --
- +----------------------------------------------------------------------+
- | NAME Christopher Sawtell |
- | SMAIL 215 Ollivier's Road, Linwood, Christchurch, 8001. New Zealand.|
- | EMAIL chris@gerty.equinox.gen.nz |
- | PHONE +64-3-389-3200 ( gmt +13 - your discretion is requested ) |
- +----------------------------------------------------------------------+
-